home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 April / CHIP_CD_2005-04.iso / software / phoa / phoa-setup-1.1.9.exe / {app} / API / phMetadata.pas next >
Pascal/Delphi Source File  |  2004-12-31  |  76KB  |  1,184 lines

  1. //**********************************************************************************************************************
  2. //  $Id: phMetadata.pas,v 1.9 2004/12/31 13:38:58 dale Exp $
  3. //----------------------------------------------------------------------------------------------------------------------
  4. //  PhoA image arranging and searching tool
  5. //  Copyright DK Software, http://www.dk-soft.org/
  6. //**********************************************************************************************************************
  7. //**********************************************************************************************************************
  8. //
  9. // Image Metadata parsing routines
  10. //
  11. // The whole code Copyright ⌐Dmitry Kann, except where otherwise explicitly noted. The EXIF format is a Japan Electronic
  12. //   Industry Development Association (JEIDA) standard, revised June 1998 as version 2.1, and is not covered by this
  13. //   copyright nor by GPL 2 terms.
  14. //
  15. // Home sites:
  16. //   http://devtools.narod.ru/
  17. //   http://phoa.narod.ru/
  18. //
  19. // Based on info from http://www.ba.wakwak.com/~tsuruzoh/
  20. //
  21. // Change log:
  22. //   Sep 08, 2003 - dale - The first open source release of the unit
  23. //   Sep 17, 2003 - dale - Added some tags. Many tags became documented thanks to JEIDA specifications
  24. //                         Added support for non-regular tags: Unicode, raw binary, Version etc.
  25. //                         The code officially became covered with GPL 2 License, either supplied along with this unit,
  26. //                         or available at http://www.gnu.org/copyleft/gpl.html
  27. //                         PLEASE NOTE:
  28. //                           The GPL 2 License does not permit incorporating this code into proprietary programs. If you
  29. //                           find the unit useful and wish to compile it within a non-opensource code, please contact me
  30. //                           at devtools@narod.ru
  31. //
  32. //   Jun 09, 2004 - dale - Removed dependency from the phUtils unit
  33. //   Jun 11, 2004 - dale - Fixed a stupid error when an IFD with zero component count supplied. Fixed rational value
  34. //                         representation (was presented as a reciprocal before)
  35. //
  36. // Disclaimer:
  37. //   The software included comes with ABSOLUTELY NO WARRANTY. You may use it only at your own risk.
  38. //
  39. //**********************************************************************************************************************
  40. unit phMetadata;
  41.  
  42. interface
  43. uses SysUtils, Windows, Classes;
  44.  
  45. type
  46.    // Pointer to Metadata Instance, a specific need in PhoA
  47.   PImageMetadata = ^TImageMetadata;
  48.  
  49.    // Image Metadata retrieval object
  50.   TImageMetadata = class(TObject)
  51.   private
  52.      // True if any call failed
  53.     FFailed: Boolean;
  54.      // True if any data were fetched from the stream
  55.     FDataFetched: Boolean;
  56.      // Prop storage
  57.     FStatusCode: Integer;
  58.     FEXIFData: TStrings;
  59.      // Loading metadata from the stream
  60.     procedure LoadFromStream(Stream: TStream);
  61.      // Reads a two-byte data from the stream. If bMotorola=True, then swaps the LSB and MSB to provide an
  62.      //   Intel-byte-ordered word
  63.     function  FetchWord(Stream: TStream; bMotorola: Boolean): Word;
  64.      // Reads a marker data size and returns or discards (skips) the marker data from the stream
  65.     function  FetchMarkerData(Stream: TStream; bSkip: Boolean): String;
  66.      // Parses EXIF marker data
  67.     procedure ParseEXIFMarkerData(const sData: String);
  68.      // Sets FStatusCode. Any code but IMS_OK causes FFailed to be set to True
  69.     procedure SetStatusCode(iCode: Integer);
  70.      // Processes an IFD at given offset. bMotorola denotes whether Motorola byte-order is used.
  71.      //   Returns offset to the next IFD, if there's any, else 0
  72.     function ProcessIFD(const sData: String; iOffset: Integer; bMotorola: Boolean): Integer;
  73.   public
  74.     constructor Create(const sFileName: String);
  75.     destructor Destroy; override;
  76.      // Props
  77.     property EXIFData: TStrings read FEXIFData;
  78.      // -- One of the IMS_xxx constants, see below. Can use metadata only if StatusCode = IMS_OK
  79.     property StatusCode: Integer read FStatusCode;
  80.   end;
  81.  
  82.    // An IFD entry
  83.   PIFDEntry = ^TIFDEntry;
  84.   TIFDEntry = packed record
  85.     wTag:     Word;    // Tag ID
  86.     wFmt:     Word;    // Format (datatype) code
  87.     iCompCnt: Integer; // Component count
  88.     iData:    Integer; // Data or offset to data (if data length>4)
  89.   end;
  90.  
  91.    // The type of EXIF tag value
  92.   TEXIFTagValueType = (
  93.     xtvtRegular,     // Regular value: numbers are numbers, characters are characters etc.
  94.     xtvtUserComment, // Used for UserComment ($9286): first 8 bytes specify the charset, the next are character codes, whether one- or two-byte (depending on charset)
  95.     xtvtCharVersion, // Data are character codes, each char is separated with a period so they're version number
  96.     xtvtUnicode,     // Data are double-byte Unicode chars
  97.     xtvtBinary);     // Data are raw binary bytes
  98.  
  99.    // An EXIF tag
  100.   PEXIFTag = ^TEXIFTag; 
  101.   TEXIFTag = record
  102.     iTag:   Integer;           // Image's EXIF tag ID
  103.     VType:  TEXIFTagValueType; // Value type
  104.     sName:  AnsiString;        // Tag name
  105.     sVList: AnsiString;        // Value list in the format: 'value1=name1:value2=name2:...'
  106.     sDesc:  AnsiString;        // Tag description
  107.   end;
  108.  
  109. const
  110.    // Metadata discrepancies/errors
  111.   IMS_OK                       = 0000; // Metadata successfully parsed
  112.   IMS_CannotOpenFile           = 0001; // Unable to open file (file doesn't exist, access denied etc)
  113.   IMS_ReadFailure              = 0002; // Unable to read from file (EOF encountered, media error, access denied etc)
  114.   IMS_NotAJPEGFile             = 0010; // The file doesn't appear to be a JPEG file
  115.   IMS_NoMetadata               = 0011; // No metadata encountered in the file
  116.   IMS_InvalidEXIFHeader        = 0100; // EXIF header doesn't match
  117.   IMS_InvalidTIFFHeader        = 0101; // TIFF header (inside EXIF header) invalid or missing
  118.  
  119.   IMS_Unknown                  = 9999; // Unknown or internal error (possibly unhandled exception)
  120.  
  121.    // IFD Entry's data type
  122.   iedUByte                     = 0001;
  123.   iedAscii                     = 0002;
  124.   iedUShort                    = 0003;
  125.   iedULong                     = 0004;
  126.   iedURational                 = 0005;
  127.   iedSByte                     = 0006;
  128.   iedUndef                     = 0007;
  129.   iedSShort                    = 0008;
  130.   iedSLong                     = 0009;
  131.   iedSRational                 = 0010;
  132.   iedSingleFloat               = 0011;
  133.   iedDoubleFloat               = 0012;
  134.  
  135.    // IFD Entries' sizes
  136.   aIFDEntSize: Array[1..12] of Integer = (1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8);
  137.  
  138.    // JPEG Markers
  139.   JM_SOF0                      = $FFC0; // Start Of Frame N, N indicates compression process
  140.   JM_SOF1                      = $FFC1;
  141.   JM_SOF2                      = $FFC2;
  142.   JM_SOF3                      = $FFC3;
  143.   JM_DHT                       = $FFC4; // Define Huffman Table
  144.   JM_SOF5                      = $FFC5;
  145.   JM_SOF6                      = $FFC6;
  146.   JM_SOF7                      = $FFC7;
  147.   JM_SOF9                      = $FFC9;
  148.   JM_SOF10                     = $FFCA;
  149.   JM_SOF11                     = $FFCB;
  150.   JM_SOF13                     = $FFCD;
  151.   JM_DAC                       = $FFCC; // Define arithmetic coding conditioning
  152.   JM_SOF14                     = $FFCE;
  153.   JM_SOF15                     = $FFCF;
  154.   JM_SOI                       = $FFD8; // Start Of Image (beginning of datastream)
  155.   JM_EOI                       = $FFD9; // End Of Image (end of datastream)
  156.   JM_SOS                       = $FFDA; // Start Of Scan (begins compressed data)
  157.   JM_DQT                       = $FFDB; // Define Quantization Table
  158.   JM_DNL                       = $FFDC; // Define Number of Lines
  159.   JM_DRI                       = $FFDD; // Restart Interoperability Definition
  160.   JM_DHP                       = $FFDE; // Define Hierarchical Progression
  161.   JM_EXP                       = $FFDF; // Expand reference component
  162.   JM_JFIF                      = $FFE0; // JFIF marker
  163.   JM_EXIF                      = $FFE1; // EXIF marker
  164.   JM_EXIFEXT                   = $FFE2; // EXIF extended marker
  165.   JM_IPTC                      = $FFED; // IPTC (Photoshop datastream)
  166.   JM_APP14                     = $FFEE; // Photoshop data: App14
  167.   JM_COM                       = $FFFE; // Comment
  168.  
  169.    // EXIF section header
  170.   SEXIFHeader                  = 'Exif'#00#00;
  171.   STIFFIntelHeader             = 'II'#$2A#00;
  172.   STIFFMotorolaHeader          = 'MM'#00#$2A;
  173.  
  174.    // EXIF Tags
  175.   EXIF_TAG_SUBIFD              = $8769;
  176.   EXIF_TAG_INTEROPIFD          = $a005;
  177.  
  178.   EXIF_TAG_DATETIME            = $0132;
  179.   EXIF_TAG_DATETIME_ORIGINAL   = $9003;
  180.   EXIF_TAG_DATETIME_DIGITIZED  = $9004;
  181.  
  182.   EXIF_TAG_ORIENTATION         = $0112;
  183.  
  184.    // Some tags are taken from http://lxr.php.net/source/php4/ext/EXIF/EXIF.c, other from
  185.    //   http://www.ba.wakwak.com/~tsuruzoh/, and some from MSDN,
  186.    //   http://msdn.microsoft.com/library/en-us/gdicpp/GDIPlus 
  187.   aEXIFTags: Array[0..256] of TEXIFTag = (
  188.     (iTag: $0001; VType: xtvtRegular;     sName: 'Interoperability Index';               sVList: ''; sDesc: ''),
  189.     (iTag: $0002; VType: xtvtCharVersion; sName: 'Interoperability Version';             sVList: '';
  190.       sDesc: 'Records the interoperability version. "01.00" means version 1.00'),
  191.     (iTag: $000b; VType: xtvtRegular;     sName: 'ACD Comment';                          sVList: ''; sDesc: ''),
  192.     (iTag: $00fe; VType: xtvtRegular;     sName: 'New Subfile Type';                     sVList: '';
  193.       sDesc: 'Type of data in a subfile'),
  194.     (iTag: $00ff; VType: xtvtRegular;     sName: 'Subfile Type';                         sVList: '';
  195.       sDesc: 'Type of data in a subfile'),
  196.     (iTag: $0100; VType: xtvtRegular;     sName: 'Image Width';                          sVList: '';
  197.       sDesc: 'Original image width (number of pixels per row)'),
  198.     (iTag: $0101; VType: xtvtRegular;     sName: 'Image Height';                         sVList: '';
  199.       sDesc: 'Original image height (number of pixel rows)'),
  200.     (iTag: $0102; VType: xtvtRegular;     sName: 'Bits Per Sample';                      sVList: '';
  201.       sDesc:
  202.         'When image format is no compression, this value shows the number of bits per component for each pixel. '+
  203.         'Usually this value is "8,8,8"'),
  204.     (iTag: $0103; VType: xtvtRegular;     sName: 'Compression';                          sVList: '6=JPEG:3=Uncompressed:1=TIFF';
  205.       sDesc: 'Shows compression method used for the image data'),
  206.     (iTag: $0106; VType: xtvtRegular;     sName: 'Photometric Interpretation';           sVList: '1=Monochrome:2=RGB:6=YCbCr';
  207.       sDesc: 'Shows the color space of the image data components'),
  208.     (iTag: $0107; VType: xtvtRegular;     sName: 'ThreshHolding';                        sVList: '';
  209.       sDesc: 'Technique used to convert from gray pixels to black and white pixels'),
  210.     (iTag: $0108; VType: xtvtRegular;     sName: 'PropertyTagCellWidth';                 sVList: '';
  211.       sDesc: 'Width of the dithering or halftoning matrix'),
  212.     (iTag: $0109; VType: xtvtRegular;     sName: 'PropertyTagCellHeight';                sVList: '';
  213.       sDesc: 'Height of the dithering or halftoning matrix'),
  214.     (iTag: $010a; VType: xtvtRegular;     sName: 'FillOrder';                            sVList: '';
  215.       sDesc: 'Logical order of bits in a byte'),
  216.     (iTag: $010d; VType: xtvtRegular;     sName: 'Document Name';                        sVList: '';
  217.       sDesc: 'Specifies the name of the document from which the image was scanned'),
  218.     (iTag: $010e; VType: xtvtRegular;     sName: 'Image Description';                    sVList: '';
  219.       sDesc: 'Specifies the title of the image'),
  220.     (iTag: $010f; VType: xtvtRegular;     sName: 'Make';                                 sVList: '';
  221.       sDesc: 'Shows manufacturer of the equipment used to record the image'),
  222.     (iTag: $0110; VType: xtvtRegular;     sName: 'Model';                                sVList: '';
  223.       sDesc: 'Shows model name or model number of the equipment used to record the image'),
  224.     (iTag: $0111; VType: xtvtRegular;     sName: 'Strip Offsets';                        sVList: '';
  225.       sDesc:
  226.         'When image format is no compression, this value shows offset to image data. In some case image data is '+
  227.         'striped and this value is plural'),
  228.     (iTag: $0112; VType: xtvtRegular;     sName: 'Orientation';
  229.       sVList:
  230.         '1=1 - Portrait (Top Left):2=2 - Portrait (Top Right):3=3 - Portrait (Bottom Right):4=4 - Portrait (Bottom Left):'+
  231.         '5=5 - Landscape (Left Top):6=6 - Landscape (Right Top):7=7 - Landscape (Right Bottom):8=8 - Landscape (Left Bottom)';
  232.       sDesc:
  233.         'The orientation of the camera relative to the scene, when the image was captured. The relation is shown for '+
  234.         'the "0th row" of image to visual position'),
  235.     (iTag: $0115; VType: xtvtRegular;     sName: 'Samples Per Pixel';                    sVList: '';
  236.       sDesc:
  237.         'When image format is no compression, this value shows the number of components stored for each pixel. For '+
  238.         'color image this value is 3'),
  239.     (iTag: $0116; VType: xtvtRegular;     sName: 'Rows Per Strip';                       sVList: '';
  240.       sDesc:
  241.         'When image format is no compression and image has stored as strip, this value shows how many rows stored to '+
  242.         'each strip. If image has not striped, this value is the same as Image Height'),
  243.     (iTag: $0117; VType: xtvtRegular;     sName: 'Strip Byte Counts';                    sVList: '';
  244.       sDesc:
  245.         'When image format is no compression and stored as strip, this value shows how many bytes used for each strip '+
  246.         'and this value is plural. If image has not striped, this value is single and means whole data size of the '+
  247.         'image'),
  248.     (iTag: $0118; VType: xtvtRegular;     sName: 'Min Sample Value';                     sVList: '';
  249.       sDesc: 'For each color component, the minimum value assigned to that component'),
  250.     (iTag: $0119; VType: xtvtRegular;     sName: 'Max Sample Value';                     sVList: '';
  251.       sDesc: 'For each color component, the maximum value assigned to that component'),
  252.     (iTag: $011a; VType: xtvtRegular;     sName: 'X-Resolution';                         sVList: '';
  253.       sDesc:
  254.         'Display/Print resolution of image in the image width (x) direction. The default value is 72 dpi'),
  255.     (iTag: $011b; VType: xtvtRegular;     sName: 'Y-Resolution';                         sVList: '';
  256.       sDesc:
  257.         'Display/Print resolution of image in the image height (y) direction. The default value is 72 dpi'),
  258.     (iTag: $011c; VType: xtvtRegular;     sName: 'Planar Configuration';                 sVList: '';
  259.       sDesc:
  260.         'When image format is no compression YCbCr, this value shows byte aligns of YCbCr data. If value is 1, '+
  261.         'Y/Cb/Cr value is chunky format, contiguous for each subsampling pixel. If value is 2, Y/Cb/Cr value is '+
  262.         'separated and stored to Y plane/Cb plane/Cr plane format'),
  263.     (iTag: $011d; VType: xtvtRegular;     sName: 'Page Name';                            sVList: '';
  264.       sDesc: 'Specifies the name of the page from which the image was scanned'),
  265.     (iTag: $011e; VType: xtvtRegular;     sName: 'X-Position';                           sVList: '';
  266.       sDesc:
  267.         'Offset from the left side of the page to the left side of the image. The unit of measure is specified by '+
  268.         'Resolution Unit'),
  269.     (iTag: $011f; VType: xtvtRegular;     sName: 'Y-Position';                           sVList: '';
  270.       sDesc:
  271.         'Offset from the top edge of the page to the top edge of the image. The unit of measure is specified by '+
  272.         'Resolution Unit'),
  273.     (iTag: $0120; VType: xtvtRegular;     sName: 'Free Offsets';                         sVList: '';
  274.       sDesc: 'For each string of contiguous unused bytes, the byte offset of that string'),
  275.     (iTag: $0121; VType: xtvtRegular;     sName: 'Free Byte Counts';                     sVList: '';
  276.       sDesc: 'For each string of contiguous unused bytes, the number of bytes in that string'),
  277.     (iTag: $0122; VType: xtvtRegular;     sName: 'Gray Reponse Unit';
  278.       sVList: '1=1/10:2=1/100:3=1/1000:4=1/10000:5=1/100000:6=1/1000000';
  279.       sDesc: 'Precision of the number specified by Gray Response Curve'),
  280.     (iTag: $0123; VType: xtvtRegular;     sName: 'Gray Reponse Curve';                   sVList: '';
  281.       sDesc: 'For each possible pixel value in a grayscale image, the optical density of that pixel value'),
  282.     (iTag: $0124; VType: xtvtRegular;     sName: 'T4 Options';                           sVList: '';
  283.       sDesc: 'Set of flags that relate to T4 encoding'),
  284.     (iTag: $0125; VType: xtvtRegular;     sName: 'T6 Options';                           sVList: '';
  285.       sDesc: 'Set of flags that relate to T6 encoding'),
  286.     (iTag: $0128; VType: xtvtRegular;     sName: 'Resolution Unit';                      sVList: '1=None Specified:2=Inch:3=Centimeter';
  287.       sDesc: 'Unit of measure for the horizontal resolution and the vertical resolution'),
  288.     (iTag: $0129; VType: xtvtRegular;     sName: 'Page Number';                          sVList: '';
  289.       sDesc: 'Page number of the page from which the image was scanned'),
  290.     (iTag: $012d; VType: xtvtRegular;     sName: 'Transfer Function';                    sVList: '';
  291.       sDesc: 'Tables that specify transfer functions for the image'),
  292.     (iTag: $0131; VType: xtvtRegular;     sName: 'Software';                             sVList: '';
  293.       sDesc: 'Specifies the name and version of the software or firmware of the device used to generate the image'),
  294.     (iTag: $0132; VType: xtvtRegular;     sName: 'Date & Time';                          sVList: '';
  295.       sDesc:
  296.         'Date/Time of image was created or last modified. If clock wasn''t set or digicam doesn''t have clock, the '+
  297.         'field may be empty. Usually the same value as Original Date & Time'),
  298.     (iTag: $013b; VType: xtvtRegular;     sName: 'Artist';                               sVList: '';
  299.       sDesc: 'Specifies the name of the person who created the image'),
  300.     (iTag: $013c; VType: xtvtRegular;     sName: 'Host Computer';                        sVList: '';
  301.       sDesc: 'Specifies the computer and/or operating system used to create the image'),
  302.     (iTag: $013d; VType: xtvtRegular;     sName: 'Predictor';                            sVList: '';
  303.       sDesc: 'Type of prediction scheme that was applied to the image data before the encoding scheme was applied'),
  304.     (iTag: $013e; VType: xtvtRegular;     sName: 'White Point';                          sVList: '';
  305.       sDesc:
  306.         'Defines chromaticity of white point of the image. If the image uses CIE Standard Illumination D65 (known as '+
  307.         'international standard of "daylight"), the values are 3127/10000 & 3290/10000'),
  308.     (iTag: $013f; VType: xtvtRegular;     sName: 'Primary Chromaticities';               sVList: '';
  309.       sDesc:
  310.         'Defines chromaticity for each of the three primary colors in the image. If the image uses CCIR '+
  311.         'Recommendation 709 primaries, values are 640/1000, 330/1000, 300/1000, 600/1000, 150/1000, 0/1000'),
  312.     (iTag: $0140; VType: xtvtRegular;     sName: 'Color Map';                            sVList: '';
  313.       sDesc: 'Color palette (lookup table) for a palette-indexed image (consisting of 16-bit words)'),
  314.     (iTag: $0141; VType: xtvtRegular;     sName: 'Half Tone Hints';                      sVList: '';
  315.       sDesc: 'Information used by the halftone function'),
  316.     (iTag: $0142; VType: xtvtRegular;     sName: 'Tile Width';                           sVList: '';
  317.       sDesc: 'Number of pixel columns in each tile'),
  318.     (iTag: $0143; VType: xtvtRegular;     sName: 'Tile Length';                          sVList: '';
  319.       sDesc: 'Number of pixel rows in each tile'),
  320.     (iTag: $0144; VType: xtvtRegular;     sName: 'Tile Offsets';                         sVList: '';
  321.       sDesc: 'For each tile, the byte offset of that tile'),
  322.     (iTag: $0145; VType: xtvtRegular;     sName: 'Tile Byte Counts';                     sVList: '';
  323.       sDesc: 'For each tile, the number of bytes in that tile'),
  324.     (iTag: $014a; VType: xtvtRegular;     sName: 'SubIFDs';                              sVList: ''; sDesc: ''),
  325.     (iTag: $014c; VType: xtvtRegular;     sName: 'Ink Set';                              sVList: '';
  326.       sDesc: 'Set of inks used in a separated image'),
  327.     (iTag: $014d; VType: xtvtRegular;     sName: 'Ink Names';                            sVList: '';
  328.       sDesc:
  329.         'Sequence of concatenated, null-terminated, character strings that specify the names of the inks used in a '+
  330.         'separated image'),
  331.     (iTag: $014e; VType: xtvtRegular;     sName: 'Number Of Inks';                       sVList: '';
  332.       sDesc: 'Number of inks'),
  333.     (iTag: $0150; VType: xtvtRegular;     sName: 'Dot Range';                            sVList: '';
  334.       sDesc: 'Color component values that correspond to a 0 percent dot and a 100 percent dot'),
  335.     (iTag: $0151; VType: xtvtRegular;     sName: 'Target Printer';                       sVList: '';
  336.       sDesc: 'Describes the intended printing environment'),
  337.     (iTag: $0152; VType: xtvtRegular;     sName: 'Extra Sample';                         sVList: '';
  338.       sDesc: 'Number of extra color components. For example, one extra component might hold an alpha value'),
  339.     (iTag: $0153; VType: xtvtRegular;     sName: 'Sample Format';                        sVList: '';
  340.       sDesc: 'For each color component, the numerical format (unsigned, signed, floating point) of that component'),
  341.     (iTag: $0154; VType: xtvtRegular;     sName: 'SMin Sample Value';                    sVList: '';
  342.       sDesc: 'For each color component, the minimum value of that component'),
  343.     (iTag: $0155; VType: xtvtRegular;     sName: 'SMax Sample Value';                    sVList: '';
  344.       sDesc: 'For each color component, the maximum value of that component'),
  345.     (iTag: $0156; VType: xtvtRegular;     sName: 'Transfer Range';                       sVList: '';
  346.       sDesc: 'Table of values that extends the range of the transfer function'),
  347.     (iTag: $0157; VType: xtvtRegular;     sName: 'Clip Path';                            sVList: ''; sDesc: ''),
  348.     (iTag: $0158; VType: xtvtRegular;     sName: 'X-Clip Path Units';                    sVList: ''; sDesc: ''),
  349.     (iTag: $0159; VType: xtvtRegular;     sName: 'Y-Clip Path Units';                    sVList: ''; sDesc: ''),
  350.     (iTag: $015a; VType: xtvtRegular;     sName: 'Indexed';                              sVList: ''; sDesc: ''),
  351.     (iTag: $015b; VType: xtvtRegular;     sName: 'JPEG Tables';                          sVList: ''; sDesc: ''),
  352.     (iTag: $015f; VType: xtvtRegular;     sName: 'OPI Proxy';                            sVList: ''; sDesc: ''),
  353.     (iTag: $0200; VType: xtvtRegular;     sName: 'JPEG Proc';                            sVList: '';
  354.       sDesc: 'When image format is JPEG - the JPEG compression process'),
  355.     (iTag: $0201; VType: xtvtRegular;     sName: 'JPEG Interchange Format';              sVList: '';
  356.       sDesc: 'When image format is JPEG - offset to JPEG bitstream'),
  357.     (iTag: $0202; VType: xtvtRegular;     sName: 'JPEG Interchange Format Length';       sVList: '';
  358.       sDesc: 'When image format is JPEG - length, in bytes, of the JPEG bitstream'),
  359.     (iTag: $0203; VType: xtvtRegular;     sName: 'JPEG Restart Interval';                sVList: '';
  360.       sDesc: 'When image format is JPEG - length of the restart interval'),
  361.     (iTag: $0205; VType: xtvtRegular;     sName: 'JPEG Lossless Predictors';             sVList: '';
  362.       sDesc:
  363.         'When image format is JPEG - for each color component, a lossless predictor-selection value for that component'),
  364.     (iTag: $0206; VType: xtvtRegular;     sName: 'JPEG Point Transforms';                sVList: '';
  365.       sDesc: 'When image format is JPEG - for each color component, a point transformation value for that component'),
  366.     (iTag: $0207; VType: xtvtRegular;     sName: 'JPEG QTables';                         sVList: '';
  367.       sDesc:
  368.         'When image format is JPEG - for each color component, the offset to the quantization table for that component'),
  369.     (iTag: $0208; VType: xtvtRegular;     sName: 'JPEG DCTables';                        sVList: '';
  370.       sDesc:
  371.         'When image format is JPEG - for each color component, the offset to the DC Huffman table (or lossless '+
  372.         'Huffman table) for that component'),
  373.     (iTag: $0209; VType: xtvtRegular;     sName: 'JPEG ACTables';                        sVList: '';
  374.       sDesc:
  375.         'When image format is JPEG - for each color component, the offset to the AC Huffman table for that component'),
  376.     (iTag: $0211; VType: xtvtRegular;     sName: 'YCbCr Coefficients';                   sVList: '';
  377.       sDesc:
  378.         'When image format is YCbCr, this value shows a constant to translate it to RGB format. As usual values are '+
  379.         '0.299, 0.587, 0.114'),
  380.     (iTag: $0212; VType: xtvtRegular;     sName: 'YCbCr Subsampling';                    sVList: '';
  381.       sDesc:
  382.         'When image format is YCbCr and uses subsampling (cropping of chroma data, all digicams do that), shows how '+
  383.         'many chroma data subsampled. First value shows horizontal, next value shows vertical subsample rate'),
  384.     (iTag: $0213; VType: xtvtRegular;     sName: 'YCbCr Positioning';                    sVList: '1=Center pixel:2=Datum point';
  385.       sDesc:
  386.         'When image format is YCbCr and uses subsampling (cropping of chroma data, all digicams do that), defines the '+
  387.         'chroma sample point of subsampling pixel array'),
  388.     (iTag: $0214; VType: xtvtRegular;     sName: 'Reference Black&White';                sVList: '';
  389.       sDesc:
  390.         'Shows reference value of black point and white point. In case of YCbCr format, first 2 show black/white of '+
  391.         'Y, next 2 are Cb, last 2 are Cr. In case of RGB format, first 2 show black/white of R, next 2 are G, last 2 '+
  392.         'are B'),
  393.     (iTag: $02bc; VType: xtvtRegular;     sName: 'Extensible Metadata Platform';         sVList: ''; sDesc: ''),
  394.     (iTag: $0301; VType: xtvtRegular;     sName: 'Gamma';                                sVList: '';
  395.       sDesc:
  396.         'Gamma value attached to the image. The gamma value is stored as a rational number (pair of long) with a '+
  397.         'numerator of 100000. For example, a gamma value of 2.2 is stored as the pair (100000, 45455)'),
  398.     (iTag: $0302; VType: xtvtRegular;     sName: 'ICC Profile Descriptor';               sVList: '';
  399.       sDesc: 'Identifies an ICC profile'),
  400.     (iTag: $0303; VType: xtvtRegular;     sName: 'SRGB Rendering Intent';
  401.       sVList: '0=Perceptual:1=Relative colorimetric:2=Saturation:3=Absolute colorimetric';
  402.       sDesc:
  403.         'How the image should be displayed as defined by the International Color Consortium (ICC):'#13+
  404.         ' ò Perceptual intent, which is suitable for photographs, gives good adaptation to the display device gamut '+
  405.         'at the expense of colorimetric accuracy.'#13+
  406.         ' ò Relative colorimetric intent is suitable for images (for example, logos) that require color appearance '+
  407.         'matching that is relative to the display device white point.'#13+
  408.         ' ò Saturation intent, which is suitable for charts and graphs, preserves saturation at the expense of hue '+
  409.         'and lightness.'#13+
  410.         ' ò Absolute colorimetric intent is suitable for proofs (previews of images destined for a different display '+
  411.         'device) that require preservation of absolute colorimetry.'),
  412.     (iTag: $0304; VType: xtvtRegular;     sName: 'Image Title';                          sVList: '';
  413.       sDesc: 'Specifies the title of the image'),
  414.     (iTag: $0320; VType: xtvtRegular;     sName: 'Image Title';                          sVList: '';
  415.       sDesc: 'Specifies the title of the image'),
  416.     (iTag: $1000; VType: xtvtRegular;     sName: 'Related Image File Format';            sVList: '';
  417.       sDesc: 'Records the file format of image file'),
  418.     (iTag: $1001; VType: xtvtRegular;     sName: 'Related Image Width';                  sVList: '';
  419.       sDesc: 'Shows image width'),
  420.     (iTag: $1002; VType: xtvtRegular;     sName: 'Related Image Height';                 sVList: '';
  421.       sDesc: 'Shows image heigth'),
  422.     (iTag: $5001; VType: xtvtRegular;     sName: 'Resolution X-Unit';                    sVList: '1=pixels per inch:2=pixels per centimeter';
  423.       sDesc: 'Units in which to display horizontal resolution'),
  424.     (iTag: $5002; VType: xtvtRegular;     sName: 'Resolution Y-Unit';                    sVList: '1=pixels per inch:2=pixels per centimeter';
  425.       sDesc: 'Units in which to display vertical resolution'),
  426.     (iTag: $5003; VType: xtvtRegular;     sName: 'Resolution X-Length Unit';
  427.       sVList: '1=inches:2=centimeters:3=points:4=picas:5=columns';
  428.       sDesc: 'Units in which to display the image width'),
  429.     (iTag: $5004; VType: xtvtRegular;     sName: 'Resolution Y-Length Unit';
  430.       sVList: '1=inches:2=centimeters:3=points:4=picas:5=columns';
  431.       sDesc: 'Units in which to display the image height'),
  432.     (iTag: $5005; VType: xtvtRegular;     sName: 'Print Flags';                          sVList: '';
  433.       sDesc: 'Sequence of one-byte Boolean values that specify printing options'),
  434.     (iTag: $5006; VType: xtvtRegular;     sName: 'Print Flags Version';                  sVList: ''; 
  435.       sDesc: 'Print flags version'),
  436.     (iTag: $5007; VType: xtvtRegular;     sName: 'Print Flags Crop';                     sVList: ''; 
  437.       sDesc: 'Print flags center crop marks'),
  438.     (iTag: $5008; VType: xtvtRegular;     sName: 'Print Flags Bleed Width';              sVList: ''; 
  439.       sDesc: 'Print flags bleed width'),
  440.     (iTag: $5009; VType: xtvtRegular;     sName: 'Print Flags Bleed Width Scale';        sVList: ''; 
  441.       sDesc: 'Print flags bleed width scale'),
  442.     (iTag: $500a; VType: xtvtRegular;     sName: 'Halftone LPI';                         sVList: ''; 
  443.       sDesc: 'Ink''s screen frequency, in lines per inch'),
  444.     (iTag: $500b; VType: xtvtRegular;     sName: 'Halftone LPI Unit';                    sVList: '1=lines per inch:2=lines per centimeter';
  445.       sDesc: 'Units for the screen frequency'),
  446.     (iTag: $500c; VType: xtvtRegular;     sName: 'Halftone Degree';                      sVList: ''; 
  447.       sDesc: 'Angle for screen'),
  448.     (iTag: $500d; VType: xtvtRegular;     sName: 'Halftone Shape';
  449.       sVList: '0=Round:1=Ellipse:2=Line:3=Square:4=Cross:6=Diamond';
  450.       sDesc: 'Shape of the halftone dots'),
  451.     (iTag: $500e; VType: xtvtRegular;     sName: 'Halftone Misc';                        sVList: '';
  452.       sDesc: 'Miscellaneous halftone information'),
  453.     (iTag: $500f; VType: xtvtRegular;     sName: 'Halftone Screen';                      sVList: '1=Use printer''s default screens:2=Other';
  454.       sDesc: 'Boolean value that specifies whether to use the printer''s default screens'),
  455.     (iTag: $5010; VType: xtvtRegular;     sName: 'JPEG Quality';                         sVList: '';
  456.       sDesc: 'Private tag used by the Adobe Photoshop format. Not for public use'),
  457.     (iTag: $5011; VType: xtvtRegular;     sName: 'Grid Size';                            sVList: '';
  458.       sDesc: 'Block of information about grids and guides'),
  459.     (iTag: $5012; VType: xtvtRegular;     sName: 'Thumbnail Format';                     sVList: '0=Raw RGB:1=JPEG';
  460.       sDesc: 'Format of the thumbnail image'),
  461.     (iTag: $5013; VType: xtvtRegular;     sName: 'Thumbnail Width';                      sVList: '';
  462.       sDesc: 'Width, in pixels, of the thumbnail image'),
  463.     (iTag: $5014; VType: xtvtRegular;     sName: 'Thumbnail Height';                     sVList: '';
  464.       sDesc: 'Height, in pixels, of the thumbnail image'),
  465.     (iTag: $5015; VType: xtvtRegular;     sName: 'Thumbnail ColorDepth';                 sVList: '';
  466.       sDesc: 'Bits per pixel (BPP) for the thumbnail image'),
  467.     (iTag: $5016; VType: xtvtRegular;     sName: 'Thumbnail Planes';                     sVList: '';
  468.       sDesc: 'Number of color planes for the thumbnail image'),
  469.     (iTag: $5017; VType: xtvtRegular;     sName: 'Thumbnail Raw Bytes';                  sVList: '';
  470.       sDesc: 'Byte offset between rows of pixel data'),
  471.     (iTag: $5018; VType: xtvtRegular;     sName: 'Thumbnail Size';                       sVList: '';
  472.       sDesc: 'Total size, in bytes, of the thumbnail image'),
  473.     (iTag: $5019; VType: xtvtRegular;     sName: 'Thumbnail CompressedSize';             sVList: '';
  474.       sDesc: 'Compressed size, in bytes, of the thumbnail image'),
  475.     (iTag: $501a; VType: xtvtRegular;     sName: 'Color Transfer Function';              sVList: '';
  476.       sDesc: 'Table of values that specify color transfer functions'),
  477.     (iTag: $501b; VType: xtvtRegular;     sName: 'Thumbnail Data';                       sVList: '';
  478.       sDesc: 'Raw thumbnail bits in JPEG or RGB format. Depends on Thumbnail Format'),
  479.     (iTag: $5020; VType: xtvtRegular;     sName: 'Thumbnail Image Width';                sVList: '';
  480.       sDesc: 'Number of pixels per row in the thumbnail image'),
  481.     (iTag: $5021; VType: xtvtRegular;     sName: 'Thumbnail Image Height';               sVList: '';
  482.       sDesc: 'Number of pixel rows in the thumbnail image'),
  483.     (iTag: $5022; VType: xtvtRegular;     sName: 'Thumbnail Bits Per Sample';            sVList: '';
  484.       sDesc: 'Number of bits per color component in the thumbnail image'),
  485.     (iTag: $5023; VType: xtvtRegular;     sName: 'Thumbnail Compression';                sVList: '';
  486.       sDesc: 'Compression method used for thumbnail image data'),
  487.     (iTag: $5024; VType: xtvtRegular;     sName: 'Thumbnail Photometric Interpretation'; sVList: '';
  488.       sDesc: 'How thumbnail pixel data will be interpreted'),
  489.     (iTag: $5025; VType: xtvtRegular;     sName: 'Thumbnail Image Description';          sVList: '';
  490.       sDesc: 'Specifies the title of the image'),
  491.     (iTag: $5026; VType: xtvtRegular;     sName: 'Thumbnail Equipment Make';             sVList: '';
  492.       sDesc: 'Specifies the manufacturer of the equipment used to record the thumbnail image'),
  493.     (iTag: $5027; VType: xtvtRegular;     sName: 'Thumbnail Equipment Model';            sVList: '';
  494.       sDesc: 'Specifies the model name or model number of the equipment used to record the thumbnail image'),
  495.     (iTag: $5028; VType: xtvtRegular;     sName: 'Thumbnail Strip Offsets';              sVList: '';
  496.       sDesc: 'For each strip in the thumbnail image, the byte offset of that strip'),
  497.     (iTag: $5029; VType: xtvtRegular;     sName: 'Thumbnail Orientation';
  498.       sVList:
  499.         '1=Portrait (Top Left):2=Portrait (Top Right):3=Portrait (Bottom Right):4=Portrait (Bottom Left):'+
  500.         '5=Landscape (Left Top):6=Landscape (Right Top):7=Landscape (Right Bottom):8=Landscape (Left Bottom)';
  501.       sDesc: 'Thumbnail image orientation'),
  502.     (iTag: $502a; VType: xtvtRegular;     sName: 'Thumbnail Samples Per Pixel';          sVList: '';
  503.       sDesc: 'Number of color components per pixel in the thumbnail image'),
  504.     (iTag: $502b; VType: xtvtRegular;     sName: 'Thumbnail Rows Per Strip';             sVList: '';
  505.       sDesc: 'Number of rows per strip in the thumbnail image'),
  506.     (iTag: $502c; VType: xtvtRegular;     sName: 'Thumbnail Strip Bytes Count';          sVList: '';
  507.       sDesc: 'For each thumbnail image strip, the total number of bytes in that strip'),
  508.     (iTag: $502d; VType: xtvtRegular;     sName: 'Thumbnail X-Resolution';               sVList: '';
  509.       sDesc: 'Thumbnail resolution in the width direction. The resolution unit is given in Thumbnail Resolution Unit'),
  510.     (iTag: $502e; VType: xtvtRegular;     sName: 'Thumbnail Y-Resolution';               sVList: '';
  511.       sDesc: 'Thumbnail resolution in the height direction. The resolution unit is given in Thumbnail Resolution Unit'),
  512.     (iTag: $502f; VType: xtvtRegular;     sName: 'Thumbnail Planar Configuration';       sVList: '';
  513.       sDesc: 'Whether pixel components in the thumbnail image are recorded in chunky or planar format'),
  514.     (iTag: $5030; VType: xtvtRegular;     sName: 'Thumbnail Resolution Unit';            sVList: '1=None Specified:2=Inch:3=Centimeter';
  515.       sDesc: 'Unit of measure for the horizontal resolution and the vertical resolution of the thumbnail image'),
  516.     (iTag: $5031; VType: xtvtRegular;     sName: 'Thumbnail Transfer Function';          sVList: ''; 
  517.       sDesc: 'Tables that specify transfer functions for the thumbnail image'),
  518.     (iTag: $5032; VType: xtvtRegular;     sName: 'Thumbnail Software Used';              sVList: '';
  519.       sDesc:
  520.         'Specifies the name and version of the software or firmware of the device used to generate the thumbnail image'),
  521.     (iTag: $5033; VType: xtvtRegular;     sName: 'Thumbnail Date & Time';                sVList: ''; 
  522.       sDesc: 'Date and time the thumbnail image was created or last modified'),
  523.     (iTag: $5034; VType: xtvtRegular;     sName: 'Thumbnail Artist';                     sVList: ''; 
  524.       sDesc: 'Specifies the name of the person who created the thumbnail image'),
  525.     (iTag: $5035; VType: xtvtRegular;     sName: 'Thumbnail White Point';                sVList: ''; 
  526.       sDesc: 'Chromaticity of the white point of the thumbnail image'),
  527.     (iTag: $5036; VType: xtvtRegular;     sName: 'Thumbnail Primary Chromatics';         sVList: ''; 
  528.       sDesc: 'For each of the three primary colors in the thumbnail image, the chromaticity of that color'),
  529.     (iTag: $5037; VType: xtvtRegular;     sName: 'Thumbnail YCbCr Coefficients';         sVList: '';
  530.       sDesc: 'Coefficients for transformation from RGB to YCbCr data for the thumbnail image'),
  531.     (iTag: $5038; VType: xtvtRegular;     sName: 'Thumbnail YCbCr Subsampling';          sVList: ''; 
  532.       sDesc: 'Sampling ratio of chrominance components in relation to the luminance component for the thumbnail image'),
  533.     (iTag: $5039; VType: xtvtRegular;     sName: 'Thumbnail YCbCr Positioning';          sVList: ''; 
  534.       sDesc: 'Position of chrominance components in relation to the luminance component for the thumbnail image'),
  535.     (iTag: $503a; VType: xtvtRegular;     sName: 'Thumbnail Ref Black&White';            sVList: ''; 
  536.       sDesc: 'Reference black point value and reference white point value for the thumbnail image'),
  537.     (iTag: $503b; VType: xtvtRegular;     sName: 'Thumbnail Copyright';                  sVList: ''; 
  538.       sDesc: 'Contains copyright information for the thumbnail image'),
  539.     (iTag: $5090; VType: xtvtRegular;     sName: 'Luminance Table';                      sVList: ''; 
  540.       sDesc:
  541.         'Luminance table. The luminance table and the chrominance table are used to control JPEG quality. '+
  542.         'A valid luminance or chrominance table has 64 entries. If an image has either a luminance table or a '+
  543.         'chrominance table, then it must have both tables'),
  544.     (iTag: $5091; VType: xtvtRegular;     sName: 'Chrominance Table';                    sVList: '';
  545.       sDesc:
  546.         'Chrominance table. The luminance table and the chrominance table are used to control JPEG quality. '+
  547.         'A valid luminance or chrominance table has 64 entries. If an image has either a luminance table or a '+
  548.         'chrominance table, then it must have both tables'),
  549.     (iTag: $5100; VType: xtvtRegular;     sName: 'Frame Delay';                          sVList: ''; 
  550.       sDesc: 'Time delay, in hundredths of a second, between two frames in an animated GIF image'),
  551.     (iTag: $5101; VType: xtvtRegular;     sName: 'Loop Count';                           sVList: '';
  552.       sDesc:
  553.         'For an animated GIF image, the number of times to display the animation. A value of 0 specifies that the '+
  554.         'animation should be displayed infinitely'),
  555.     (iTag: $5102; VType: xtvtRegular;     sName: 'Global Palette';                       sVList: '';
  556.       sDesc: 'Color palette for an indexed bitmap in a GIF image'),
  557.     (iTag: $5103; VType: xtvtRegular;     sName: 'Index Background';                     sVList: '';
  558.       sDesc: 'Index of the background color in the palette of a GIF image'),
  559.     (iTag: $5104; VType: xtvtRegular;     sName: 'IndexTransparent';                     sVList: '';
  560.       sDesc: 'Index of the transparent color in the palette of a GIF image'),
  561.     (iTag: $5110; VType: xtvtRegular;     sName: 'Pixel Unit';                           sVList: '0=Unknown';
  562.       sDesc: 'Unit for Pixel Per Unit (X) and Pixel Per Unit (Y)'),
  563.     (iTag: $5111; VType: xtvtRegular;     sName: 'Pixel Per Unit (X)';                   sVList: ''; 
  564.       sDesc: 'Pixels per unit in the x direction'),
  565.     (iTag: $5112; VType: xtvtRegular;     sName: 'Pixel Per Unit (Y)';                   sVList: '';
  566.       sDesc: 'Pixels per unit in the y direction'),
  567.     (iTag: $5113; VType: xtvtRegular;     sName: 'Palette Histogram';                    sVList: ''; 
  568.       sDesc: 'Palette histogram'),
  569.     (iTag: $800d; VType: xtvtRegular;     sName: 'Image ID';                             sVList: ''; sDesc: ''),
  570.     (iTag: $80e3; VType: xtvtRegular;     sName: 'Matteing';                             sVList: ''; sDesc: ''),
  571.     (iTag: $80e4; VType: xtvtRegular;     sName: 'Data Type';                            sVList: ''; sDesc: ''),
  572.     (iTag: $80e5; VType: xtvtRegular;     sName: 'Image Depth';                          sVList: ''; sDesc: ''),
  573.     (iTag: $80e6; VType: xtvtRegular;     sName: 'Tile Depth';                           sVList: ''; sDesc: ''),
  574.     (iTag: $828d; VType: xtvtRegular;     sName: 'CFA Repeat Pattern Dim';               sVList: ''; sDesc: ''),
  575.     (iTag: $828e; VType: xtvtRegular;     sName: 'CFA Pattern';                          sVList: ''; sDesc: ''),
  576.     (iTag: $828f; VType: xtvtRegular;     sName: 'Battery Level';                        sVList: ''; sDesc: ''),
  577.     (iTag: $8298; VType: xtvtRegular;     sName: 'Copyright';                            sVList: '';
  578.       sDesc: 'Contains copyright information'),
  579.     (iTag: $829a; VType: xtvtRegular;     sName: 'Exposure Time (sec)';                  sVList: '';
  580.       sDesc: 'Exposure time (reciprocal of shutter speed). Unit is second'),
  581.     (iTag: $829d; VType: xtvtRegular;     sName: 'F-Number';                             sVList: '';
  582.       sDesc: 'The actual F-Number (F-stop) of lens when the image was taken'),
  583.     (iTag: $83bb; VType: xtvtRegular;     sName: 'IPTC/NAA';                             sVList: ''; sDesc: ''),
  584.     (iTag: $84e3; VType: xtvtRegular;     sName: 'IT8 Raster Padding';                   sVList: ''; sDesc: ''),
  585.     (iTag: $84e5; VType: xtvtRegular;     sName: 'IT8 Color Table';                      sVList: ''; sDesc: ''),
  586.     (iTag: $8649; VType: xtvtRegular;     sName: 'Image Resource Information';           sVList: ''; sDesc: ''),
  587.     (iTag: $8769; VType: xtvtRegular;     sName: 'EXIF SubIFD Offset';                   sVList: '';
  588.       sDesc: 'File offset to EXIF SubIFD (Image File Directory)'),
  589.     (iTag: $8773; VType: xtvtRegular;     sName: 'ICC Profile';                          sVList: '';
  590.       sDesc: 'ICC profile embedded in the image'),
  591.     (iTag: $8822; VType: xtvtRegular;     sName: 'Exposure Program';
  592.       sVList:
  593.         '0=Unidentified:1=Manual:2=Normal:3=Aperture priority:4=Shutter priority:'+
  594.         '5=Creative (biased toward depth of field):6=Action (biased toward fast shutter speed):'+
  595.         '7=Portrait mode (for close-up photos with the background out of focus):'+
  596.         '8=Landscape mode (for landscape photos with the background in focus)';
  597.       sDesc: 'Exposure program that the camera used when image was taken'),
  598.     (iTag: $8824; VType: xtvtRegular;     sName: 'Spectral Sensitivity';                 sVList: '';
  599.       sDesc:
  600.         'Specifies the spectral sensitivity of each channel of the camera used. The string is compatible with the '+
  601.         'standard developed by the ASTM Technical Committee'),
  602.     (iTag: $8825; VType: xtvtRegular;     sName: 'GPS IFD Offset';                       sVList: '';
  603.       sDesc: 'Offset to a block of GPS property items'),
  604.     (iTag: $8827; VType: xtvtRegular;     sName: 'ISO Speed Ratings';                    sVList: '';
  605.       sDesc: 'ISO speed and ISO latitude of the camera or input device as specified in ISO 12232'),
  606.     (iTag: $8828; VType: xtvtRegular;     sName: 'OECF';                                 sVList: '';
  607.       sDesc:
  608.         'Optoelectronic conversion function (OECF) specified in ISO 14524. The OECF is the relationship between the '+
  609.         'camera optical input and the image values'),
  610.     (iTag: $8829; VType: xtvtRegular;     sName: 'Interlace';                            sVList: ''; sDesc: ''),
  611.     (iTag: $882a; VType: xtvtRegular;     sName: 'Time Zone Offset';                     sVList: ''; sDesc: ''),
  612.     (iTag: $882b; VType: xtvtRegular;     sName: 'Self Timer Mode';                      sVList: ''; sDesc: ''),
  613.     (iTag: $9000; VType: xtvtCharVersion; sName: 'EXIF Version';                         sVList: '';
  614.       sDesc:
  615.         'Version of the EXIF standard supported. Nonexistence of this field is taken to mean nonconformance to the '+
  616.         'standard. Conformance to the standard EXIF 2.1 is indicated by recording values 02.10'),
  617.     (iTag: $9003; VType: xtvtRegular;     sName: 'Original Date & Time';                 sVList: '';
  618.       sDesc:
  619.         'Date/Time of original image taken. This value should not be modified by user program. If clock wasn''t set '+
  620.         'or digicam doesn''t have clock, the field may be empty'),
  621.     (iTag: $9004; VType: xtvtRegular;     sName: 'Date & Time Digitized';                sVList: '';
  622.       sDesc:
  623.         'Date/Time of when the image was digitized. If clock wasn''t set or digicam doesn''t have clock, the field '+
  624.         'may be empty. Usually the same value as Original Date & Time'),
  625.     (iTag: $9101; VType: xtvtRegular;     sName: 'Components Configuration';             sVList: '';
  626.       sDesc:
  627.         'Information specific to compressed data. The channels of each component are arranged in order from the first '+
  628.         'component to the fourth. For uncompressed data, the data arrangement is given in the Photometric '+
  629.         'Interpretation tag. However, because Photometric Interpretation can only express the order of Y, Cb, and Cr, '+
  630.         'this tag is provided for cases when compressed data uses components other than Y, Cb, and Cr and to support '+
  631.         'other sequences. Most of case "0x04,0x05,0x06,0x00" is used for RGB-format and "0x01,0x02,0x03,0x00" for '+
  632.         'YCbCr-format. 0x00:does not exist, 0x01:Y, 0x02:Cb, 0x03:Cr, 0x04:Red, 0x05:Green, 0x06:Blue'),
  633.     (iTag: $9102; VType: xtvtRegular;     sName: 'Compressed Bits Per Pixel';            sVList: '';
  634.       sDesc: 'Information specific to compressed data. The average compression ratio of JPEG (rough estimate)'),
  635.     (iTag: $9201; VType: xtvtRegular;     sName: 'Shutter Speed Value';                  sVList: '';
  636.       sDesc:
  637.         'Shutter speed. The unit is the Additive System of Photographic Exposure (APEX) value. To convert this value '+
  638.         'to ordinary "Shutter Speed", calculate this value''s power of 2, then reciprocal. For example, if the value '+
  639.         'is 4, shutter speed is 1/16 second'),
  640.     (iTag: $9202; VType: xtvtRegular;     sName: 'Aperture Value';                       sVList: '';
  641.       sDesc:
  642.         'The actual aperture value of lens when the image was taken. The unit is the Additive System of Photographic '+
  643.         'Exposure (APEX) value. To convert this value to ordinary F-Number (F-Stop), calculate this value''s power of '+
  644.         'root 2 (=1.4142). For example, if the value is 5, F-Number is 1.4142^5 = F5.6'),
  645.     (iTag: $9203; VType: xtvtRegular;     sName: 'Brightness Value';                     sVList: '';
  646.       sDesc:
  647.         'Brightness of taken subject. The unit is the Additive System of Photographic Exposure (APEX) value. To '+
  648.         'calculate Exposure (Ev) from Brigtness Value (Bv), you must add Sensitivity Value (Sv). Ev=Bv+Sv; '+
  649.         'Sv=log2(ISOSpeedRating/3.125); ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32'),
  650.     (iTag: $9204; VType: xtvtRegular;     sName: 'Exposure Bias Value';                  sVList: '';
  651.       sDesc:
  652.         'Exposure bias (compensation) value of taking picture. The unit is the Additive System of Photographic '+
  653.         'Exposure (APEX) value. Ordinarily it is given in the range -99.99 to 99.99'),
  654.     (iTag: $9205; VType: xtvtRegular;     sName: 'Max Aperture Value';                   sVList: '';
  655.       sDesc:
  656.         'Maximum aperture value of lens (smallest F-number of the lens). You can convert to F-Number by calculating '+
  657.         'power of root 2 (same as Aperture Value)'),
  658.     (iTag: $9206; VType: xtvtRegular;     sName: 'Subject Distance (m)';                 sVList: '';
  659.       sDesc: 'Distance to focus point, unit is meter'),
  660.     (iTag: $9207; VType: xtvtRegular;     sName: 'Metering Mode';
  661.       sVList: '0=Unknown:1=Average:2=Center weighted average:3=Spot:4=Multi-spot:5=Multi-segment:6=Partial:255=Other';
  662.       sDesc: 'Exposure metering method'),
  663.     (iTag: $9208; VType: xtvtRegular;     sName: 'Light Source';
  664.       sVList:
  665.         '0=Unidentified:1=Daylight:2=Fluorescent:3=Tungsten:10=Flash:17=Standard light A:18=Standard light B:'+
  666.         '19=Standard light C:20=D55:21=D65:22=D75:255=Other';
  667.       sDesc: 'Light source; actually this means white balance setting'),
  668.     (iTag: $9209; VType: xtvtRegular;     sName: 'Flash';
  669.       sVList:
  670.         '0=Flash didn''t fire:1=Flash fired:5=Flash fired, strobe return light wasn''t detected:'+
  671.         '7=Flash fired, strobe return light detected';
  672.       sDesc: 'Flash usage while taking the picture'),
  673.     (iTag: $920a; VType: xtvtRegular;     sName: 'Focal Length (mm)';                    sVList: '';
  674.       sDesc:
  675.         'Actual focal length of lens used to take image. Unit is millimeter. Conversion is not made to the focal '+
  676.         'length of a 35 millimeter film camera'),
  677.     (iTag: $920b; VType: xtvtRegular;     sName: 'Flash Energy';                         sVList: ''; sDesc: ''),
  678.     (iTag: $920c; VType: xtvtRegular;     sName: 'Spatial Frequency Response';           sVList: ''; sDesc: ''),
  679.     (iTag: $920d; VType: xtvtRegular;     sName: 'Noise';                                sVList: ''; sDesc: ''),
  680.     (iTag: $920e; VType: xtvtRegular;     sName: 'Focal Plane X-Resolution';             sVList: ''; sDesc: ''),
  681.     (iTag: $920f; VType: xtvtRegular;     sName: 'Focal Plane Y-Resolution';             sVList: ''; sDesc: ''),
  682.     (iTag: $9210; VType: xtvtRegular;     sName: 'Focal Plane Resolution Unit';
  683.       sVList: '1=None Specified:2=Inch:3=Centimeter';
  684.       sDesc: ''),
  685.     (iTag: $9211; VType: xtvtRegular;     sName: 'Image Number';                         sVList: ''; sDesc: ''),
  686.     (iTag: $9212; VType: xtvtRegular;     sName: 'Security Classification';              sVList: ''; sDesc: ''),
  687.     (iTag: $9213; VType: xtvtRegular;     sName: 'Image History';                        sVList: ''; sDesc: ''),
  688.     (iTag: $9214; VType: xtvtRegular;     sName: 'Subject Location';                     sVList: ''; sDesc: ''),
  689.     (iTag: $9215; VType: xtvtRegular;     sName: 'Exposure Index';                       sVList: ''; sDesc: ''),
  690.     (iTag: $9216; VType: xtvtRegular;     sName: 'TIFF/EPStandard ID';                   sVList: ''; sDesc: ''),
  691.     (iTag: $9217; VType: xtvtRegular;     sName: 'Sensing Method';                       sVList: ''; sDesc: ''),
  692.     (iTag: $923f; VType: xtvtRegular;     sName: 'StoNits';                              sVList: ''; sDesc: ''),
  693.     (iTag: $927c; VType: xtvtBinary;      sName: 'Maker Note';                           sVList: '';
  694.       sDesc: 'Maker-dependent internal data'),
  695.     (iTag: $9286; VType: xtvtUserComment; sName: 'User Comment';                         sVList: '';
  696.       sDesc:
  697.         'Comment tag. A tag used by EXIF users to write keywords or comments about the image besides those in '+
  698.         'Image Description and without the character-code limitations of the Image Description tag'),
  699.     (iTag: $9290; VType: xtvtRegular;     sName: 'Sub Sec Time';                         sVList: '';
  700.       sDesc:
  701.         'Some of digicam can take 2 to 30 pictures per second, but "Date & Time" tag cannot record the sub-second '+
  702.         'time. This tag is used to record it'),
  703.     (iTag: $9291; VType: xtvtRegular;     sName: 'Sub Sec Time Original';                sVList: '';
  704.       sDesc:
  705.         'Some of digicam can take 2 to 30 pictures per second, but "Original Date & Time" tag cannot record the '+
  706.         'sub-second time. This tag is used to record it'),
  707.     (iTag: $9292; VType: xtvtRegular;     sName: 'Sub Sec Time Digitized';               sVList: '';
  708.       sDesc:
  709.         'Some of digicam can take 2 to 30 pictures per second, but "Date & Time Digitized" tag cannot record the '+
  710.         'sub-second time. This tag is used to record it'),
  711.     (iTag: $953c; VType: xtvtRegular;     sName: 'Image Source Data';                    sVList: ''; sDesc: ''),
  712.     (iTag: $9c9b; VType: xtvtUnicode;     sName: 'Title';                                sVList: ''; sDesc: ''),
  713.     (iTag: $9c9c; VType: xtvtUnicode;     sName: 'Comments';                             sVList: ''; sDesc: ''),
  714.     (iTag: $9c9d; VType: xtvtUnicode;     sName: 'Author';                               sVList: ''; sDesc: ''),
  715.     (iTag: $9c9e; VType: xtvtUnicode;     sName: 'Keywords';                             sVList: ''; sDesc: ''),
  716.     (iTag: $9c9f; VType: xtvtUnicode;     sName: 'Subject';                              sVList: ''; sDesc: ''),
  717.     (iTag: $a000; VType: xtvtCharVersion; sName: 'FlashPix Version';                     sVList: '';
  718.       sDesc:
  719.         'FlashPix format version supported by an FPXR file. If the image data is based on FlashPix format Ver.1.0, '+
  720.         'value is "01.00"'),
  721.     (iTag: $a001; VType: xtvtRegular;     sName: 'Color Space';                          sVList: '0=sBW:1=sRGB:65535=Uncalibrated';
  722.       sDesc:
  723.         'Defines Color Space. DCF image must use sRGB color space. If the picture uses the other color space, value '+
  724.         'is Uncalibrated. Image data recorded as Uncalibrated can be treated as sRGB when it is converted to FlashPix'),
  725.     (iTag: $a002; VType: xtvtRegular;     sName: 'EXIF Image Width';                     sVList: '';
  726.       sDesc:
  727.         'Information specific to compressed data. When a compressed file is recorded, the valid width of the '+
  728.         'meaningful image must be recorded in this tag, whether or not there is padding data or a restart marker. '+
  729.         'This tag should not exist in an uncompressed file'),
  730.     (iTag: $a003; VType: xtvtRegular;     sName: 'EXIF Image Height';                    sVList: '';
  731.       sDesc:
  732.         'Information specific to compressed data. When a compressed file is recorded, the valid height of the '+
  733.         'meaningful image must be recorded in this tag whether or not there is padding data or a restart marker. '+
  734.         'This tag should not exist in an uncompressed file. Because data padding is unnecessary in the vertical '+
  735.         'direction, the number of lines recorded in this valid image height tag will be the same as that recorded in '+
  736.         'the SOF'),
  737.     (iTag: $a004; VType: xtvtRegular;     sName: 'Related Sound File';                   sVList: '';
  738.       sDesc:
  739.         'The name of an audio file related to the image data. The only relational information recorded is the EXIF '+
  740.         'audio file name and extension (an ASCII string that consists of 8 characters plus a period (.), plus 3 '+
  741.         'characters). The path is not recorded. When you use this tag, audio files must be recorded in conformance '+
  742.         'with the EXIF audio format. Writers can also store audio data within APP2 as FlashPix extension stream data'),
  743.     (iTag: $a005; VType: xtvtRegular;     sName: 'InteroperabilityIFD Offset';           sVList: '';
  744.       sDesc: 'Extension of "ExifR98", details are unknown. This value is offset to IFD format data'),
  745.     (iTag: $a20b; VType: xtvtRegular;     sName: 'Flash Energy';                         sVList: '';
  746.       sDesc: 'Strobe energy, in Beam Candle Power Seconds (BCPS), at the time the image was captured'),
  747.     (iTag: $a20c; VType: xtvtRegular;     sName: 'Spatial Frequency Response';           sVList: '';
  748.       sDesc:
  749.         'Camera or input device spatial frequency table and SFR values in the image width, image height, and '+
  750.         'diagonal direction, as specified in ISO 12233'),
  751.     (iTag: $a20e; VType: xtvtRegular;     sName: 'Focal Plane X-Resolution';             sVList: '';
  752.       sDesc:
  753.         'Pixel density at CCD''s position. If you have MegaPixel digicam and take a picture by lower resolution '+
  754.         '(e.g. VGA mode), this value is re-sampled by picture resolution. In such case, Focal Plane Resolution is '+
  755.         'not the same as CCD''s actual resolution'),
  756.     (iTag: $a20f; VType: xtvtRegular;     sName: 'Focal Plane Y-Resolution';             sVList: ''; 
  757.       sDesc:
  758.         'Pixel density at CCD''s position. If you have MegaPixel digicam and take a picture by lower resolution '+
  759.         '(e.g. VGA mode), this value is re-sampled by picture resolution. In such case, Focal Plane Resolution is '+
  760.         'not the same as CCD''s actual resolution'),
  761.     (iTag: $a210; VType: xtvtRegular;     sName: 'Focal Plane Resolution Unit';          sVList: '1=None Specified:2=Inch:3=Centimeter';
  762.       sDesc: 'Unit of Focal Plane X-Resoluton & Focal Plane Y-Resolution'),
  763.     (iTag: $a211; VType: xtvtRegular;     sName: 'Image Number';                         sVList: ''; sDesc: ''),
  764.     (iTag: $a212; VType: xtvtRegular;     sName: 'Security Classification';              sVList: ''; sDesc: ''),
  765.     (iTag: $a213; VType: xtvtRegular;     sName: 'Image History';                        sVList: '';
  766.       sDesc: 'Specifies the history of the image'),
  767.     (iTag: $a214; VType: xtvtRegular;     sName: 'Subject Location';                     sVList: '';
  768.       sDesc:
  769.         'Location of the main subject in the scene. The value of this tag represents the pixel at the center of the '+
  770.         'main subject relative to the left edge. The first value indicates the column number, and the second value '+
  771.         'indicates the row number'),
  772.     (iTag: $a215; VType: xtvtRegular;     sName: 'Exposure Index';                       sVList: '';
  773.       sDesc: 'Same as ISO Speed Ratings. Only Kodak''s digicams use this tag instead of ISO Speed Rating'),
  774.     (iTag: $a216; VType: xtvtRegular;     sName: 'TIFF/EPStandard ID';                   sVList: ''; sDesc: ''),
  775.     (iTag: $a217; VType: xtvtRegular;     sName: 'Sensing Method';
  776.       sVList:
  777.         '0=Unknown:1=Monochrome Area:2=One Chip Color Area:3=Two Chip Color Area:4=Three Chip Color Area:'+
  778.         '5=Color Sequential Area:6=Monochrome Linear:7=Trilinear:8=Color Sequential Linear';
  779.       sDesc: 'Shows type of image sensor unit'),
  780.     (iTag: $a300; VType: xtvtRegular;     sName: 'File Source';                          sVList: '1=Unknown:3=Digital Still Camera';
  781.       sDesc: 'Indicates the image source'),
  782.     (iTag: $a301; VType: xtvtRegular;     sName: 'Scene Type';                           sVList: '0=Unknown:1=Directly Photographed';
  783.       sDesc: 'Indicates the type of scene'),
  784.     (iTag: $a302; VType: xtvtRegular;     sName: 'CFA Pattern';                          sVList: '';
  785.       sDesc:
  786.         'Indicates the Color Filter Array (CFA) geometric pattern of the image sensor when a one-chip color area '+
  787.         'sensor is used. It does not apply to all sensing methods'),
  788.     (iTag: $a401; VType: xtvtRegular;     sName: 'Custom Rendered';                      sVList: '0=Normal process:1=Custom process'; sDesc: ''),
  789.     (iTag: $a402; VType: xtvtRegular;     sName: 'Exposure Mode';                        sVList: '0=Auto:1=Manual:2=Auto bracket'; sDesc: ''),
  790.     (iTag: $a403; VType: xtvtRegular;     sName: 'White Balance';                        sVList: '0=Auto:1=Manual'; sDesc: ''),
  791.     (iTag: $a404; VType: xtvtRegular;     sName: 'Digital Zoom Ratio';                   sVList: ''; sDesc: ''),
  792.     (iTag: $a405; VType: xtvtRegular;     sName: 'Focal Length In 35mm Film';            sVList: ''; sDesc: ''),
  793.     (iTag: $a406; VType: xtvtRegular;     sName: 'Scene Capture Type';
  794.       sVList: '0=Standard:1=Landscape:2=Portrait:3=Night scene';
  795.       sDesc: ''),
  796.     (iTag: $a407; VType: xtvtRegular;     sName: 'Gain Control';
  797.       sVList: '0=None:1=Low gain up:2=High gain up:3=Low gain down:4=High gain down';
  798.       sDesc: ''),
  799.     (iTag: $a408; VType: xtvtRegular;     sName: 'Contrast';                             sVList: '0=Normal:1=Soft:2=Hard'; sDesc: ''),
  800.     (iTag: $a409; VType: xtvtRegular;     sName: 'Saturation';                           sVList: '0=Normal:1=Low:2=High'; sDesc: ''),
  801.     (iTag: $a40a; VType: xtvtRegular;     sName: 'Sharpness';                            sVList: '0=Normal:1=Soft:2=Hard'; sDesc: ''),
  802.     (iTag: $a40b; VType: xtvtRegular;     sName: 'Device Setting Description';           sVList: ''; sDesc: ''),
  803.     (iTag: $a40c; VType: xtvtRegular;     sName: 'Subject Distance Range';
  804.       sVList: '0=Unknown:1=Macro:2=Close view:3=Distant view';
  805.       sDesc: ''),
  806.     (iTag: $a420; VType: xtvtRegular;     sName: 'Image Unique ID';                      sVList: '0=Close view:1=Distant view'; sDesc: ''));
  807.  
  808.    // Returns pointer to EXIF tag entry, nil if not found
  809.   function FindEXIFTag(iTag: Integer): PEXIFTag;
  810.  
  811. implementation
  812.  
  813.    // Intel-to-Motorola (and back) byte-order data conversion
  814.  
  815.   function IMTranslateWord(w: Word): Word;
  816.   begin
  817.     Result := (w shl 8) or (w shr 8);
  818.   end;
  819.  
  820.   function IMTranslateInt(i: Integer): Integer;
  821.   begin
  822.     Result := (i shl 24) or (i shl 8 and $00ff0000) or (i shr 8 and $0000ff00) or (i shr 24);
  823.   end;
  824.  
  825.   function IMTranslateRational(i64: Int64): Int64;
  826.   begin
  827.     Result := Int64(IMTranslateInt(i64 shr 32)) shl 32 or IMTranslateInt(i64);
  828.   end;
  829.  
  830.    // Misc routines
  831.  
  832.   function FindEXIFTag(iTag: Integer): PEXIFTag;
  833.   var i: Integer;
  834.   begin
  835.     for i := 0 to High(aEXIFTags) do begin
  836.       Result := @aEXIFTags[i];
  837.       if Result^.iTag=iTag then Exit;
  838.     end;
  839.     Result := nil;
  840.   end;
  841.   
  842.   function GetFirstWord(const s, sDelimiters: String): String;
  843.   var i: Integer;
  844.   begin
  845.     i := 1;
  846.     while (i<=Length(s)) and (Pos(s[i], sDelimiters)=0) do Inc(i);
  847.     Result := Copy(s, 1, i-1);
  848.   end;
  849.  
  850.   function ExtractFirstWord(var s: String; const sDelimiters: String): String;
  851.   begin
  852.     Result := GetFirstWord(s, sDelimiters);
  853.     Delete(s, 1, Length(Result)+1);
  854.   end;
  855.  
  856.   procedure AccumulateStr(var s: String; const sSeparator, sAdd: String);
  857.   begin
  858.     if sAdd<>'' then begin
  859.       if s<>'' then s := s+sSeparator;
  860.       s := s+sAdd;
  861.     end;
  862.   end;
  863.  
  864.    //===================================================================================================================
  865.    // TImageMetadata
  866.    //===================================================================================================================
  867.  
  868.   constructor TImageMetadata.Create(const sFileName: String);
  869.   var fs: TFileStream;
  870.   begin
  871.     inherited Create;
  872.     FStatusCode := IMS_Unknown;
  873.     FEXIFData := TStringList.Create;
  874.      // Create a file stream to load data from
  875.     try
  876.       fs := TFileStream.Create(sFileName, fmOpenRead or fmShareDenyWrite);
  877.     except
  878.        // Handle any exception translating it into the appropriate StatusCode
  879.       SetStatusCode(IMS_CannotOpenFile);
  880.       fs := nil; // Satisfy the compiler
  881.     end;
  882.      // If file is open successfully
  883.     if not FFailed then
  884.       try
  885.         LoadFromStream(fs);
  886.       finally
  887.         fs.Free;
  888.       end;
  889.   end;
  890.  
  891.   destructor TImageMetadata.Destroy;
  892.   begin
  893.     FEXIFData.Free;
  894.     inherited Destroy;
  895.   end;
  896.  
  897.   function TImageMetadata.FetchMarkerData(Stream: TStream; bSkip: Boolean): String;
  898.   var wSize: Word;
  899.   begin
  900.     Result := '';
  901.      // Data size includes size-bytes' length itself
  902.     if not FFailed then begin
  903.       wSize := FetchWord(Stream, True)-2;
  904.       try
  905.         if bSkip then
  906.           Stream.Seek(wSize, soCurrent)
  907.         else begin
  908.           SetLength(Result, wSize);
  909.           Stream.ReadBuffer(Result[1], wSize);
  910.         end;
  911.       except
  912.         SetStatusCode(IMS_ReadFailure);
  913.       end;
  914.     end;
  915.   end;
  916.  
  917.   function TImageMetadata.FetchWord(Stream: TStream; bMotorola: Boolean): Word;
  918.   begin
  919.     try
  920.       Stream.ReadBuffer(Result, SizeOf(Result));
  921.     except
  922.       SetStatusCode(IMS_ReadFailure);
  923.     end;
  924.     if not FFailed and bMotorola then Result := IMTranslateWord(Result);
  925.   end;
  926.  
  927.   procedure TImageMetadata.LoadFromStream(Stream: TStream);
  928.   var wMarker: Word;
  929.   begin
  930.      // Parse markers consequently
  931.     Stream.Seek(0, soBeginning);
  932.     while not FFailed do begin
  933.       wMarker := FetchWord(Stream, True);
  934.       if not FFailed then begin
  935.          // A JPEG file *must* start with SOI marker
  936.         if not FDataFetched then begin
  937.           if wMarker<>JM_SOI then SetStatusCode(IMS_NotAJPEGFile);
  938.          // Not a first marker, go on
  939.         end else
  940.           case wMarker of
  941.              // EOI or SOS encountered, get out
  942.             JM_EOI, JM_SOS: Break;
  943.              // EXIF data to be parsed out
  944.             JM_EXIF: begin
  945.               ParseEXIFMarkerData(FetchMarkerData(Stream, False));
  946.               Exit;
  947.             end;
  948.              // All other tags are skipped
  949.             else FetchMarkerData(Stream, True);
  950.           end;
  951.         FDataFetched := True;
  952.       end;
  953.     end;
  954.      // If no metadata found
  955.     if not FFailed then SetStatusCode(IMS_NoMetadata);
  956.   end;
  957.  
  958.   procedure TImageMetadata.ParseEXIFMarkerData(const sData: String);
  959.   var
  960.     sTIFFHeader, sRaw: String;
  961.     bMotorolaOrder: Boolean;
  962.     iIFDOffset: Integer;
  963.   begin
  964.      // Check EXIF header
  965.     if (Length(sData)<6+8) or (Copy(sData, 1, 6)<>SEXIFHeader) then
  966.       SetStatusCode(IMS_InvalidEXIFHeader)
  967.     else begin
  968.        // Check TIFF header / Get TIFF byte order
  969.       sTIFFHeader := Copy(sData, 7, 4);
  970.       if sTIFFHeader=STIFFIntelHeader then
  971.         bMotorolaOrder := False
  972.       else if sTIFFHeader=STIFFMotorolaHeader then
  973.         bMotorolaOrder := True
  974.       else begin
  975.         SetStatusCode(IMS_InvalidTIFFHeader);
  976.         Exit;
  977.       end;
  978.        // Get IFD Offset
  979.       Move(sData[11], iIFDOffset, 4);
  980.       if bMotorolaOrder then iIFDOffset := IMTranslateInt(iIFDOffset);
  981.        // Process chained IFDs. Offset is computed relative to the TIFF header
  982.       sRaw := Copy(sData, 7, MaxInt);
  983.       while iIFDOffset<>0 do iIFDOffset := ProcessIFD(sRaw, iIFDOffset, bMotorolaOrder);
  984.        // Set status to indicate success
  985.       if not FFailed then SetStatusCode(IMS_OK);
  986.     end;
  987.   end;
  988.  
  989.   function TImageMetadata.ProcessIFD(const sData: String; iOffset: Integer; bMotorola: Boolean): Integer;
  990.   var
  991.     iDataLen, iEntDataSize, iComponentSize, i: Integer;
  992.     wEntCount, wEntry: Word;
  993.     IE: TIFDEntry;
  994.     pt: PEXIFTag;
  995.     sCompValue, sTagValue: String;
  996.     i64Component: Int64;
  997.  
  998.      // Retrieves data component at specified Index (0..iCompCount-1). Returns True if retrieval succeeded
  999.     function GetComponent(iIndex: Integer; out i64CompValue: Int64): Boolean;
  1000.     var ptr: Pointer;
  1001.     begin
  1002.       i64CompValue := 0;
  1003.        // Value smaller than or equal to 4 bytes size is stored directly in IE.iData
  1004.       if iEntDataSize<=4 then
  1005.         ptr := @IE.iData
  1006.        // For longer data iData is the offset. Check data size 
  1007.       else if IE.iData+iComponentSize>iDataLen then begin
  1008.         Result := False;
  1009.         Exit;
  1010.       end else
  1011.         ptr := @sData[IE.iData+1];
  1012.        // Move pointer to specified index
  1013.       Inc(Integer(ptr), iComponentSize*iIndex);
  1014.        // Get data, convert from Motorola byte-order if required (only data retrieved through offset)
  1015.       Move(ptr^, i64CompValue, iComponentSize);
  1016.       if bMotorola and (iEntDataSize>4) then
  1017.         case IE.wFmt of
  1018.           {2} iedUShort, iedSShort:       i64CompValue := IMTranslateWord(Word(i64CompValue));
  1019.           {4} iedULong, iedSLong:         i64CompValue := IMTranslateInt(Integer(i64CompValue));
  1020.           {8} iedURational, iedSRational: i64CompValue := IMTranslateRational(i64CompValue);
  1021.         end;
  1022.       Result := True;
  1023.     end;
  1024.  
  1025.      // Translates numeric Value to string
  1026.     function GetComponentAsString(i64Value: Int64): String;
  1027.     var
  1028.       sValue: Single absolute i64Value;
  1029.       dValue: Double absolute i64Value;
  1030.     begin
  1031.       Result := '';
  1032.       case IE.wFmt of
  1033.         iedUByte, iedUndef: Result := IntToStr(Byte(i64Value));
  1034.         iedAscii:           if i64Value and $ff<>0 then Result := Char(i64Value); // Skip terminating char (#0)
  1035.         iedUShort:          Result := IntToStr(Word(i64Value));
  1036.         iedULong:           Result := IntToStr(Cardinal(i64Value));
  1037.         iedURational:       Result := Format('%d/%d', [Cardinal(i64Value), Cardinal(i64Value shr 32)]);
  1038.         iedSByte:           Result := IntToStr(ShortInt(i64Value));
  1039.         iedSShort:          Result := IntToStr(SmallInt(i64Value));
  1040.         iedSLong:           Result := IntToStr(Integer(i64Value));
  1041.         iedSRational:       Result := Format('%d/%d', [Integer(i64Value), Integer(i64Value shr 32)]);
  1042.         iedSingleFloat:     Result := FloatToStr(sValue);
  1043.         iedDoubleFloat:     Result := FloatToStr(dValue);
  1044.       end;
  1045.     end;
  1046.  
  1047.      // Decodes component value according to decoding list
  1048.     function DecodeComponentValue(const sValue: String): String;
  1049.     var s, sExpr: String;
  1050.     begin
  1051.       s := pt^.sVList;
  1052.       repeat
  1053.         sExpr := ExtractFirstWord(s, ':');
  1054.         if sValue=ExtractFirstWord(sExpr, '=') then begin
  1055.           Result := sExpr;
  1056.           Exit;
  1057.         end;
  1058.       until s='';
  1059.       Result := sValue;
  1060.     end;
  1061.  
  1062.      // Translates a buffer string containing Unicode characters to Ansi using the current system charset
  1063.     function UnicodeBufToAnsi(const sBuffer: String): String;
  1064.     var
  1065.       ws: WideString;
  1066.       iLen: Integer;
  1067.     begin
  1068.       iLen := Length(sBuffer) div 2;
  1069.        // Move wide chars to ws
  1070.       SetLength(ws, iLen);
  1071.       Move(sBuffer[1], ws[1], iLen*2);
  1072.        // Translate wide chars to Ansi chars
  1073.       SetLength(Result, iLen);
  1074.       WideCharToMultiByte(CP_ACP, 0, @ws[1], iLen, @Result[1], iLen, nil, nil);
  1075.        // Trim control chars & null-terminator
  1076.       Result := Trim(Result); 
  1077.     end;
  1078.  
  1079.      // Processes data of xtvtUserComment format
  1080.     function FormatUserComment(const sBuffer: String): String;
  1081.     var sCode, sValue: String;
  1082.     begin
  1083.        // The first 8 bytes indicate charset. The rest are value
  1084.       sCode  := Copy(sTagValue, 1, 7);
  1085.       sValue := Copy(sTagValue, 9, MaxInt);
  1086.        // Make the only distinction for Unicode here
  1087.       if AnsiSameText(sCode, 'UNICODE') then Result := UnicodeBufToAnsi(sValue) else Result := Trim(sValue);
  1088.     end;
  1089.  
  1090.      // Formats raw binary data as hexadecimal ASCII representation (without any delimiters)
  1091.     function FormatRawBinary(const sBuffer: String): String;
  1092.     const acHexChars: Array[$0..$f] of Char = '0123456789abcdef';
  1093.     var
  1094.       i: Integer;
  1095.       b: Byte;
  1096.     begin
  1097.       i := Length(sBuffer);
  1098.       SetLength(Result, i*2);
  1099.       for i := 1 to i do begin
  1100.         b := Byte(sBuffer[i]);
  1101.         Result[i*2-1] := acHexChars[b div 16];
  1102.         Result[i*2]   := acHexChars[b mod 16];
  1103.       end;
  1104.     end;
  1105.  
  1106.   begin
  1107.     Result := 0;
  1108.     iDataLen := Length(sData);
  1109.      // Get entries count. If offset points outside the data, just skip this IFD
  1110.     if iDataLen-iOffset<2 then Exit;
  1111.     Move(sData[iOffset+1], wEntCount, 2);
  1112.     if bMotorola then wEntCount := IMTranslateWord(wEntCount);
  1113.     if wEntCount=0 then Exit;
  1114.      // Check data size. If offset points outside the data, just skip this IFD
  1115.     if iDataLen-iOffset<2+wEntCount*12+4 then Exit;
  1116.      // Iterate thru IFD's entries
  1117.     Inc(iOffset, 2);
  1118.     for wEntry := 0 to wEntCount-1 do begin
  1119.       Move(sData[iOffset+1], IE, SizeOf(IE));
  1120.        // Convert from Motorola byte-order if necessary
  1121.       if bMotorola then
  1122.         with IE do begin
  1123.           wTag     := IMTranslateWord(wTag);
  1124.           wFmt     := IMTranslateWord(wFmt);
  1125.           iCompCnt := IMTranslateInt(iCompCnt);
  1126.           iData    := IMTranslateInt(iData);
  1127.         end;
  1128.        // Try to find tag
  1129.       pt := FindEXIFTag(IE.wTag);
  1130.        // Check tag, component count and datatype
  1131.       if (pt<>nil) and (IE.iCompCnt>0) and (IE.wFmt>=1) and (IE.wFmt<=12) then begin
  1132.          // Compute data size
  1133.         iComponentSize := aIFDEntSize[IE.wFmt];
  1134.         iEntDataSize := IE.iCompCnt*iComponentSize;
  1135.          // Compose tag value
  1136.         sTagValue := '';
  1137.         for i := 0 to IE.iCompCnt-1 do begin
  1138.           if not GetComponent(i, i64Component) then begin
  1139.             sTagValue := '(error)';
  1140.             Break;
  1141.           end;
  1142.           case pt^.VType of
  1143.              // Regular value: translate it on-the-fly
  1144.             xtvtRegular: begin
  1145.               sCompValue := GetComponentAsString(i64Component);
  1146.                // If component is to be decoded
  1147.               if pt^.sVList<>'' then begin
  1148.                 sTagValue := DecodeComponentValue(sCompValue);
  1149.                 Break;
  1150.               end;
  1151.                // ASCII strings are simply concatenated, other data values are separated with comma
  1152.               if IE.wFmt=iedAscii then sTagValue := sTagValue+sCompValue else AccumulateStr(sTagValue, ',', sCompValue);
  1153.                // If the component is an IFD offset, then process this IFD
  1154.               if (IE.wTag=EXIF_TAG_SUBIFD) or (IE.wTag=EXIF_TAG_INTEROPIFD) then ProcessIFD(sData, i64Component, bMotorola);
  1155.             end;
  1156.              // Other values - to be processed later, accumulate them as binary string
  1157.             else sTagValue := sTagValue+Char(i64Component);
  1158.           end;
  1159.         end;
  1160.          // Post-process the non-regular value
  1161.         case pt^.VType of
  1162.           xtvtUserComment: sTagValue := FormatUserComment(sTagValue);
  1163.           xtvtCharVersion: sTagValue := Copy(sTagValue, 1, 2)+'.'+Copy(sTagValue, 3, MaxInt);
  1164.           xtvtUnicode:     sTagValue := UnicodeBufToAnsi(sTagValue);
  1165.           xtvtBinary:      sTagValue := FormatRawBinary(sTagValue);
  1166.         end;
  1167.          // Register value
  1168.         FEXIFData.AddObject(sTagValue, Pointer(IE.wTag));
  1169.       end;
  1170.       Inc(iOffset, 12);
  1171.     end;
  1172.      // The last four bytes are always offset to the next IFD, or zero if no one
  1173.     Move(sData[iOffset+1], Result, SizeOf(Result));
  1174.     if bMotorola then Result := IMTranslateInt(Result);
  1175.   end;
  1176.  
  1177.   procedure TImageMetadata.SetStatusCode(iCode: Integer);
  1178.   begin
  1179.     FStatusCode := iCode;
  1180.     if iCode<>IMS_OK then FFailed := True; 
  1181.   end;
  1182.  
  1183. end.
  1184.